''' <summary>
''' Provides you with functionality to use Mercurial in your application!
''' (hg.Net by Wesley Werner)
''' -- http://code.google.com/p/hgdotnet/
''' </summary>
''' <remarks></remarks>
Public Class Wrapper

	' the command object we use to execute our wrapper commands
	Private WithEvents CMD As Command


#Region " events and delegates "

	''' <summary>
	''' Occurs when command output is received
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks></remarks>
	Public Event OutputReceived(ByVal sender As Object, ByVal e As hgNet.Internals.CommandEventArgs)

	''' <summary>
	''' Cross-thread safety delegate for OutputReceived
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks></remarks>
	Private Delegate Sub OutputReceived_Delegate(ByVal sender As Object, ByVal e As hgNet.Internals.CommandEventArgs)

	''' <summary>
	''' Occurs when the command process has exited
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks></remarks>
	Public Event CommandCompleted(ByVal sender As Object, ByVal e As hgNet.Internals.CommandEventArgs)

	''' <summary>
	''' Cross-thread safety delegate for CommandCompleted
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks></remarks>
	Private Delegate Sub CommandCompleted_Delegate(ByVal sender As Object, ByVal e As hgNet.Internals.CommandEventArgs)

#End Region


#Region " properties "


	''' <summary>
	''' Gets the last command exit code
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public ReadOnly Property ExitCode() As Integer
		Get
			Return CMD.ExitCode
		End Get
	End Property


	''' <summary>
	''' The local repo location
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public Property Local_Repository() As String
		Get
			Return _Repository
		End Get
		Set(ByVal value As String)
			_Repository = value
		End Set
	End Property
	Private _Repository As String


	''' <summary>
	''' The hg version and copyright string
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public ReadOnly Property Version() As String
		Get
			Return _Version
		End Get
	End Property
	Private _Version As String


	''' <summary>
	''' The control which to synchronize threads with
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public Property SynchronizeControl() As System.Windows.Forms.Control
		Get
			Return _SynchronizeControl
		End Get
		Set(ByVal value As System.Windows.Forms.Control)
			_SynchronizeControl = value
		End Set
	End Property
	Private _SynchronizeControl As System.Windows.Forms.Control


	''' <summary>
	''' The wrapper can talk to Mercurial
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public ReadOnly Property Online() As Boolean
		Get
			Return _Online
		End Get
	End Property
	Private _Online As Boolean


	''' <summary>
	''' Additional parameters to add to the end of the command
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public Property Parameters() As List(Of String)
		Get
			Return _Parameters
		End Get
		Set(ByVal value As List(Of String))
			_Parameters = value
		End Set
	End Property
	Private _Parameters As New List(Of String)


	''' <summary>
	''' Gets the last Command output
	''' </summary>
	''' <value></value>
	''' <returns></returns>
	''' <remarks></remarks>
	Public ReadOnly Property CommandOutput() As String
		Get
			If IsNothing(Me.CMD) Then
				Return ""
			Else
				Return Me.CMD.Output
			End If
		End Get
	End Property


#End Region


#Region " initializers etc etc, etc "


	''' <summary>
	''' Initialize this hg wrapper
	''' </summary>
	''' <remarks></remarks>
	Public Sub New(ByVal local_repo As String)

		Try
			' Get the hg version and validate it is installed
			local_repository = local_repo
			_Online = False
			Setup_Command(True)
			_Version = CMD.ExecuteCommand("version")
			_Online = (_Version.Length > 0)
		Catch ex As Exception
			Throw New Exception(String.Format("Error initialising wrapper: {0}", ex.Message))
		End Try

	End Sub


#End Region


#Region " thread-safety calls "


	''' <summary>
	''' The command process has completed
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks></remarks>
	Private Sub CMD_Complete(ByVal sender As Object, ByVal e As Internals.CommandEventArgs) Handles CMD.Complete

		' test if we can synchronize threads to avoid cross-thread exceptions
		If Not IsNothing(SynchronizeControl) Then
			If SynchronizeControl.InvokeRequired Then
				' we must synchronize with the control's thread
				Dim d As New CommandCompleted_Delegate(AddressOf CMD_Complete)
				SynchronizeControl.Invoke(d, sender, e)
			Else
				' no synching required, raise it
				RaiseEvent CommandCompleted(Me, e)
			End If
		Else
			' there is no synching control, just raise it
			RaiseEvent CommandCompleted(Me, e)
		End If

	End Sub


	''' <summary>
	''' The command has raised an asynchronous output event
	''' </summary>
	''' <param name="sender"></param>
	''' <param name="e"></param>
	''' <remarks></remarks>
	Private Sub cmd_OutputReceived(ByVal sender As Object, ByVal e As hgNet.Internals.CommandEventArgs) Handles CMD.OutputReceived
		If Not IsNothing(e.Output) Then

			' test if we can synchronize threads to avoid cross-thread exceptions
			If Not IsNothing(SynchronizeControl) Then
				If SynchronizeControl.InvokeRequired Then
					' we must synchronize with the control's thread
					Dim d As New OutputReceived_Delegate(AddressOf cmd_OutputReceived)
					SynchronizeControl.Invoke(d, sender, e)
				Else
					' no synching required, raise it
					RaiseEvent OutputReceived(Me, e)
				End If
			Else
				' there is no synching control, just raise it
				RaiseEvent OutputReceived(Me, e)
			End If

		End If
	End Sub


	''' <summary>
	''' Setup the Command object
	''' </summary>
	''' <param name="force_synchronous">True to force synchronous command return, default is False (async)</param>
	''' <remarks></remarks>
	Private Sub Setup_Command(Optional ByVal force_synchronous As Boolean = False)

		' validate the repoloc
		If IsNothing(Local_Repository) Then Throw New Exception("please set your local repository")

		' create a new instance
		CMD = New Command

		' set the working directory
		CMD.WorkingDirectory = Me.Local_Repository

		' this is a hidden command, since we capture the output manually
		CMD.Hidden = True

		' use asynchronous events if there is a synchronizing control
		If (force_synchronous) Then
			CMD.ASyncOutput = False
		Else
			CMD.ASyncOutput = Not IsNothing(SynchronizeControl)
		End If

	End Sub

#End Region


#Region " internal methods "

	''' <summary>
	''' Merge and return all the given values into one array. Includes parameters in the Me.Parameters list
	''' </summary>
	''' <param name="params1"></param>
	''' <param name="params2"></param>
	''' <returns></returns>
	''' <remarks></remarks>
	Private Function MergeParameters(ByVal params1() As String, ByVal ParamArray params2() As String) As String()

		Dim p As New List(Of String)
		p.AddRange(params1)
		If Not IsNothing(params2) Then p.AddRange(params2)
		p.AddRange(Me.Parameters)
		Return p.ToArray

	End Function

	''' <summary>
	''' Merge and return all the given values into one array. Includes parameters in the Me.Parameters list
	''' </summary>
	''' <param name="command"></param>
	''' <returns></returns>
	''' <remarks></remarks>
	Private Function MergeParameters(ByVal command As String, ByVal ParamArray params2() As String) As String()
		Return Me.MergeParameters(New String() {command}, params2)
	End Function


#End Region


#Region " synchronous wrappers "

	''' <summary>
	''' Get the log for the specified file or folder
	''' </summary>
	''' <param name="path">folder or file path to query, relative to the repo root</param>
	''' <remarks></remarks>
	Public Function Log(Optional ByVal path As String = "") As List(Of FileLogResult)

		' execute and grab the output
		Setup_Command(True)
		CMD.ExecuteCommand(MergeParameters("log", "-v", path))

		' call ResultBase.Parse_Result_Blocks(output)
		Dim blocks As List(Of String)
		blocks = ResultBase.Parse_Result_Blocks(CMD.Output)

		' store the results in this list
		Dim resultList As New List(Of FileLogResult)

		' parse the output into a file log result list
		For Each resultBlock As String In blocks
			resultList.Add(New FileLogResult(resultBlock))
		Next

		' and we are done!
		Return resultList

	End Function


	''' <summary>
	''' Show status of files in the repository. If names are given, only files that match are shown.
	''' </summary>
	''' <param name="filenames">A list of filenames to match</param>
	''' <returns></returns>
	''' <remarks></remarks>
	Public Function Status(ByVal ParamArray filenames() As String) As List(Of StatusResult)

		Setup_Command(True)
		CMD.ExecuteCommand(MergeParameters("status", filenames))

		' call ResultBase.Parse_Result_Blocks(output)
		Dim blocks As List(Of String)
		blocks = ResultBase.Parse_Result_Blocks(CMD.Output, hgNet.Constants.EOL)

		' store the results in this list
		Dim resultList As New List(Of StatusResult)

		' parse the output into a file log result list
		For Each resultBlock As String In blocks
			resultList.Add(New StatusResult(resultBlock))
		Next

		' and we are done!
		Return resultList

	End Function


#End Region


#Region " asynchronous wrappers "


	''' <summary>
	''' Execute a custom command, including any parameters
	''' This call is asynchronous
	''' </summary>
	''' <param name="command">A list containing the command with additional parameters</param>
	''' <remarks></remarks>
	Public Sub Custom(ByVal ParamArray command() As String)

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters(command))

	End Sub


	''' <summary>
	''' Create a new repository in the given directory
	''' This call is asynchronous
	''' </summary>
	''' <param name="path"></param>
	''' <remarks></remarks>
	Public Sub Init(Optional ByVal path As String = "")

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("init", path))

	End Sub


	''' <summary>
	''' Add the given file or folder to the local repo
	''' This call is asynchronous
	''' </summary>
	''' <param name="path">path of the file or folder to add, it must exists and is relative to the repo root</param>
	''' <remarks>ASyncronous</remarks>
	Public Sub Add(Optional ByVal path As String = "")

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("add", path))

	End Sub


	''' <summary>
	'''  Add all new files and remove all missing files
	''' This call is asynchronous
	''' </summary>
	''' <param name="path"></param>
	''' <remarks></remarks>
	Public Sub AddRemove(Optional ByVal path As String = "")

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("addremove", "-v", path))

	End Sub


	''' <summary>
	''' Commit files, folders or all outstanding changes
	''' This call is asynchronous
	''' </summary>
	''' <param name="path">files to commit, if omitted then all changes reported by "hg status" will be committed.</param>
	''' <remarks>ASyncronous</remarks>
	Public Sub Commit(Optional ByVal path As String = "")

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("commit", path))

	End Sub


	''' <summary>
	''' Push changes from the local repository to the given destination
	''' This call is asynchronous
	''' </summary>
	''' <param name="destination"></param>
	''' <remarks></remarks>
	Public Sub Push(Optional ByVal destination As String = "")

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("push", destination))

	End Sub


	''' <summary>
	''' Pull changes from a remote repository to a local one
	''' This call is asynchronous
	''' </summary>
	''' <param name="source"></param>
	''' <remarks></remarks>
	Public Sub Pull(Optional ByVal source As String = "")

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("pull", source))

	End Sub


	''' <summary>
	''' Create a copy of an existing repository in a specified directory
	''' This call is asynchronous
	''' </summary>
	''' <param name="repo_path">The path of the repo to clone from</param>
	''' <param name="destination">The path to clone to</param>
	''' <remarks></remarks>
	Public Sub Clone(ByVal repo_path As String, ByVal destination As String)

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("clone", repo_path, destination))

	End Sub


	''' <summary>
	''' Revert the named files or directories to the contents they had in the parent of the working directory. 
	''' </summary>
	''' <param name="filenames"></param>
	''' <remarks></remarks>
	Public Sub Revert(ByVal ParamArray filenames() As String)

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("revert", filenames))

	End Sub


	''' <summary>
	''' Revert the named files or directories to the contents they had in the parent of the working directory. 
	''' </summary>
	''' <param name="options">A list of options to pass before the list of filenames</param>
	''' <param name="filenames"></param>
	''' <remarks></remarks>
	Public Sub Revert(ByVal options() As String, ByVal ParamArray filenames() As String)

		Setup_Command()
		CMD.ExecuteCommand(MergeParameters("revert", MergeParameters(options, filenames)))

	End Sub


#End Region


End Class
